/* ----------------------------------------------------------------------------

	pax.validate.js Copyright (C) 2005, 2006, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.validate
		This is the form validation part of the PAX library
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

/*

	WIP
	---

	. Add a change detection method, to show that data has changed. Note: use field.defaultValue, see:
		http://www.comptechdoc.org/independent/web/cgi/javamanual/javaform.html
		
	. Add a "hint wizard" mode, where you can display hints one after the other, with 'back' 'cancel' and 'next' buttons.
		- This will show how to acheive something specific, kind of like a "learning mode".
		- You should be able to insert comments in between hint steps, in the "hint wizard".		
		- We have hints, so we just need a simple list of the order in which to enter the data fields,
			then a way to manage which fields have been entered correctly.
		- The wizard start would be assigned to an elemnt click
		- Each step would focus on a field, and show the message with buttons to go "Back" or "Next" at the bottom.
		- The wizard should be aware of tabsets, and be able to focus on fields in a specific tab.

	. As we now support multiple validators per field, we need a way to show 
		the message, when a validation fails, so people know what went wrong...

--- */

var pax = pax || {};
pax.validate = pax.validate || {};


pax.validate.validators = {};								// The validator methods
pax.validate.ajaxValidatorQueue = {};						// The ajax validator queue, obj shape: { oldValue: [VALUE], robj: [REQUEST OBJECT] }
pax.validate.showHintOnFocus = true;						// If we show the hint as soon as you enter the field
pax.validate.showHintOnHover = true;						// If we show the hint as you hover over the field
pax.validate.showAlertOnError = false;						// If we show an alert on error, or simply focus on field, and display a hint
pax.validate.preventSubmit = true;							// If prevent the form from being submitted, if it has errors
pax.validate.hintMessageClass = 'hintMessage';				// CSS Class to use for hint messages
pax.validate.errorMessageClass = 'errorMessage';			// CSS Class to use for error messages
pax.validate.fieldValidClass = 'validField';				// CSS Class to use for valid field
pax.validate.fieldInvalidClass = 'invalidField';			// CSS Class to use for invalid field
pax.validate.fieldToBeClass = 'tobeValidatedField';			// CSS Class to use for ajax validation fields, before they are validated


/* ----------------------------------------------------------------------------
	The validators take a field, and return true if it is valid, false if not.
---------------------------------------------------------------------------- */

/*	Method: pax.validate.validators.ip
	Validate an ip address, also checks for optional port number
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="ipaddress">
				</span>
			</form>
			[:.
				valDict = {
					'ipaddress': { mask: 'ip', hint: 'This must be a valid IP Address. You can specify a port, with <b>:[port number]</b>' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="ipaddress" value="127.0.0.1:8080">
			</span>
			<div id='pax.validate.validators.ip.example1'></div>
			[:.
				pax.$('pax.validate.validators.ip.example1').innerHTML = 'Is this a valid email address?: ' + pax.validate.validators.ip( pax.$('ipaddress') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.ip = function( field, mask ) {
	var validField = true;
	var fieldValue = pax.validate.defaultWhiteSpace( field.value, mask );
	portMin = 0;
	portMax = 99999;
	if ( !( /^\d{1,3}(\.\d{1,3}){3}(:\d+)?$/.test( fieldValue ) ) )validField = false;
	else {
		var part = fieldValue.split( /[.:]/ );
		for( var i = 0; i < part.length; i++ ) {
			if ( i == 4 ) {	// Check port
				if ( part[i] < portMin || part[i] > portMax )validField = false;
			}
			else if ( part[i] < 0 || part[i] > 255 ) {
				validField = false;
			}
		}
	}
	return validField;
};


/*	Method: pax.validate.validators.email
	Validate an email address
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<label for="emailAddress">Email:</label>
					<input type="text" name="emailAddress">
				</span>
			</form>
			[:.
				valDict = {
					'emailAddress': { mask: 'email', hint: 'This must be in email format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="emailAddress" value="bill@microsoft.com">
			</span>
			<div id='pax.validate.validators.email.example1'></div>
			[:.
				pax.$('pax.validate.validators.email.example1').innerHTML = 'Is this a valid email address?: ' + pax.validate.validators.email( pax.$('emailAddress') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.email = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	//	return value.match(/\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/gi);
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(2([0-4]\d|5[0-5])|1?\d{1,2})(\.(2([0-4]\d|5[0-5])|1?\d{1,2})){3} \])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test( value );
};


/*	Method: pax.validate.validators.alpha
	Validates an alpha value
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="alpha">
				</span>
			</form>
			[:.
				valDict = {
					'alpha': { mask: 'alpha', hint: 'This must be in alpha format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="alpha" value="qwerty">
			</span>
			<div id='pax.validate.validators.alpha.example1'></div>
			[:.
				pax.$('pax.validate.validators.alpha.example1').innerHTML = 'Is this only alpha chars?: ' + pax.validate.validators.alpha( pax.$('alpha') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.alpha = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	return ( value.match(/^[a-zA-Z]+$/gi) )? true : false;
};


/*	Method: pax.validate.validators.numeric
	Validates a numeric value
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="numeric">
				</span>
			</form>
			[:.
				valDict = {
					'numeric': { mask: 'numeric', hint: 'This must be in numeric format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="numeric" value="123456">
			</span>
			<div id='pax.validate.validators.numeric.example1'></div>
			[:.
				pax.$('pax.validate.validators.numeric.example1').innerHTML = 'Is this only numeric chars?: ' + pax.validate.validators.numeric( pax.$('numeric') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.numeric = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	return ( value.match(/^[0-9]+$/gi) )? true : false;
};


/*	Method: pax.validate.validators.alphaNumeric
	Validates an alpha numeric value
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="alphaNumeric">
				</span>
			</form>
			[:.
				valDict = {
					'alphaNumeric': { mask: 'alphaNumeric', hint: 'This must be in alphaNumeric format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="alphaNumeric" value="123456">
			</span>
			<div id='pax.validate.validators.alphaNumeric.example1'></div>
			[:.
				pax.$('pax.validate.validators.alphaNumeric.example1').innerHTML = 'Is this only alphaNumeric chars?: ' + pax.validate.validators.alphaNumeric( pax.$('alphaNumeric') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.alphaNumeric = function( field, mask ) {
	var value = pax.validate.defaultWhiteSpace( field.value, mask );
	return ( value.match(/^[a-zA-Z0-9]+$/gi) )? true : false;
};


/*	Method: pax.validate.validators.len
	Validates length of a field
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="len">
				</span>
			</form>
			[:.
				valDict = {
					'len': { mask: [ { mask: 'len', minLen: '4', maxLen: '6'} ], hint: 'This must be in at least 4 chars, and no more than 6 chars' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="len" value="12345">
			</span>
			<div id='pax.validate.validators.len.example1'></div>
			[:.
				pax.$('pax.validate.validators.len.example1').innerHTML = 'Is this only len chars?: ' + pax.validate.validators.len( pax.$('len'), { minLen: 4, maxLen: 6 } );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.len = function( field, mask ) {
	var validField = true;
	var min = ( mask.minLen != null )? mask.minLen : 0;
	var max = ( mask.maxLen != null )? mask.maxLen : 1000000;

	//	Select field length
	if( field.type.indexOf( 'select' ) > -1 ) {
		var selectedOptions = 0;
		for( var x =0; x < field.options.length; x++ ) {
			if( field.options[x].selected )selectedOptions += 1;
		}
		validField = ( selectedOptions >= min && selectedOptions <= max );
	} else {
		validField = ( field.value.length >= min && field.value.length <= max );
	}
	
	return validField
};


/*	Method: pax.validate.excludeSelect
	Excludes an option value from a select box
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<select name="exclude">
						<option value='one'>One</option>
						<option value='two'>Two</option>
						<option value='three'>Three</option>
					</select>
				</span>
			</form>
			[:.
				valDict = {
					'exclude': { mask: [ { mask: 'excludeSelect', exclude: [0] } ], hint: 'Select anything but the first option' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<select name="exclude" id="exclude">
					<option value='one'>One</option>
					<option value='two'>Two</option>
					<option value='three'>Three</option>
				</select>
			</span>
			<div id='pax.validate.validators.exclude.example1'></div>
			[:.
				pax.$('pax.validate.validators.exclude.example1').innerHTML = 'Is this a valid option?: ' + pax.validate.validators.excludeSelect( pax.$('exclude'), { exclude: [0] } );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.excludeSelect = function( field, mask ) {
	var validField = true;
	var exclude = ( mask.exclude != null )? mask.exclude : [];

	//	Select disallow options
	if( field.type.indexOf( 'select' ) > -1 ) {
		for( var x = 0; x < field.options.length; x++ ) {
			if( field.options[x].selected ) {
				for( var e = 0; e < exclude.length; e++ ) {
					if( x == exclude[e] ) {
						validField = false;
						break;
					}
				}
			}
		}
	} else {
		validField = true;
	}
	
	return validField
};


/*	Method: pax.validate.validators.range
	Validates range of the value of a field
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="rangeField">
				</span>
			</form>
			[:.
				valDict = {
					'rangeField': { mask: [ { mask: 'range', min: '10', max: '50'} ], hint: 'This must be in at least 10, and no more than 50' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="rangeField2" value="30">
			</span>
			<div id='pax.validate.validators.range.example1'></div>
			[:.
				pax.$('pax.validate.validators.range.example1').innerHTML = 'Is this within range?: ' + pax.validate.validators.range( pax.$('rangeField2'), { min: 20, max: 40 } );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.range = function( field, mask ) {
	var min = ( mask.min != null )? mask.min : false;
	var max = ( mask.max != null )? mask.max : false;
	var value = parseInt( field.value ) || 0;
	//	TODO: fail, if we can't parse int here.
	return ( value >= min && value <= max );
};


/*	Method: pax.validate.validators.fieldEqual
	Validates that the value of one field is the same as another
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise
		
	Example:
		(start code)
			<form id='valForm' name='valForm'>
				<label for='pass1'>Password: </label><input type='text' name='pass1'/>
				<label for='pass2'>Re-enter password: </label><input type='text' name='pass2'/>
				<input type='submit'/>
			</form>
			<div id='pax.validate.validators.len.example2'></div>
			[:.
				var valDict = {
					'pass1': { 
						mask: [ 
							{ mask: 'len', minLen: '6', maxLen: '12'},
							{ mask: 'checkPasswords', field2: 'pass2' } 
						], hint: 'Please enter a pasword between 6 and 12 chars, you must renter the same password in the password 2 box.' }
				};  

				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This will validate the two password fields contain the same value
*/
pax.validate.validators.fieldEqual = function( field, mask ) {
	var field2 = pax.$(mask.field) || document.forms[field.formName][mask.field];
	return ( field.value == field2.value)? true : false;
};


/*	Method: pax.validate.validators.notEmpty
	Validates contents of a field
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="notEmptyField">
				</span>
			</form>
			[:.
				valDict = {
					'notEmptyField': { mask: 'notEmpty', hint: 'This field must have a value specified' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input name="level" type="radio" value="one"> 1
					<input name="level" type="radio" value="two"> 2
					<input name="level" type="radio" value="three"> 3
				</span>
			</form>
			[:.
				valDict = {
					'level': { mask: 'notEmpty', hint: 'You must check a level' }
				};

				pax.validate.initValidation( 'valForm', valDict );  
			:]
		(end)
		This would set a validator on the radio fields, and display a hint with the specified text.
		It should be noted that the formName is requied to validate radio fields, ie: you can't validate
		radio fields outside a form.
		
	Example:
		(start code)
			<span>
				<input type="text" id="notEmptyField2" value="12345">
			</span>
			<div id='pax.validate.validators.len.example2'></div>
			[:.
				pax.$('pax.validate.validators.len.example2').innerHTML = 'Is this field not empty?: ' + pax.validate.validators.notEmpty( pax.$('notEmptyField2') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.notEmpty = function( field, mask ) {
	var validField = false;
	if( field.type == 'radio' || field.type == 'checkbox' ) {
		if( typeof field.formName != 'undefined' ) {
			if( field.formName ) {
				rfields = document.forms[field.formName][field.name];
				for( var r = 0; r < rfields.length; r++ ) {
					if( rfields[r].checked )validField = true;
				}
			}
		}
	} else if( field.type.indexOf( 'select' ) > -1 ) {
		//	Note: In IE, using field.value won't work, if the select box options don't have a value attribute, so we MUST always use a seletc box properly.
		validField = (field.value != '' && field.value != 'undefined' && field.value != null);
	} else {
		validField = (field.value != '' && field.value != 'undefined' && field.value != null);
	}
	
	return validField;
};


/*	Method: pax.validate.validators.postcode
	Validates an Australian post code
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="postcode">
				</span>
			</form>
			[:.
				valDict = {
					'postcode': { mask: 'postcode', hint: 'This must be in postcode format' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="postcode" value="2444">
			</span>
			<div id='pax.validate.validators.postcode.example1'></div>
			[:.
				pax.$('pax.validate.validators.postcode.example1').innerHTML = 'Is this a valid postcode?: ' + pax.validate.validators.postcode( pax.$('postcode') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.postcode = function( field, mask ) {
	return ( field.value.match(/^[0-9]{4}$/gi) )? true : false;
};


/*	Method: pax.validate.validators.luhn
	Validates a number with the luhn algorithm. Note that this is used in CC validation, and IMEI validation.
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="number">
				</span>
			</form>
			[:.
				valDict = {
					'number': { mask: 'luhn', hint: 'This must be a number that has a valid luhn check digit' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="number" value="4242424242424242">
			</span>
			<div id='pax.validate.validators.luhn.example1'></div>
			[:.
				pax.$('pax.validate.validators.luhn.example1').innerHTML = 'Is this a valid luhn check digit number?: ' + pax.validate.validators.luhn( pax.$('number') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.luhn = function( field, mask ) {
	var value = field.value;
	if( ! pax.validate.validators.numeric( { value: value } ) )return false;
	
	var sum = 0;
	var alt = false;
	var numvar = 0;
	for( var i = value.length - 1; i >= 0; i-- ) {
		numvar = parseInt( value.charAt(i) );
		if( alt ) {
			numvar *= 2;
			if( numvar > 9 )numvar -= 9;
		}
		sum += numvar;
		alt = !alt;
	}
	return ( sum % 10 == 0 );
};


/*	Method: pax.validate.validators.imei
	Validates a IMEI number using luhn algorithm
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="imei" id="imei">
				</span>
			</form>
			[:.
				valDict = {
					'imei': { mask: 'imei', hint: 'This must be a valid IMEI number' },
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
*/
pax.validate.validators.imei = function( field ) {
	var value = field.value;
	value = value.split(' ').join('').split('-').join('');	//	remove spaces and dashes
	if( ! ( value.length == 15 ) )return false;
	return pax.validate.validators.luhn( { value: value } );
};



/*	Method: pax.validate.validators.creditcard
	Validates a credit card number; optionally validates card type
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="cc" id="cc">
				</span>
			</form>
			[:.
				valDict = {
					'cc': { mask: [ { mask: 'creditcard', cardtype: 'Visa' } ], hint: 'This must be a valid VISA Credit card number' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		
	Example:
		(start code)
			<form name='valForm'>
				<span>
					<select name="card_type" id="card_type">
						<option>Mastercard</option>
						<option>Visa</option>
						<option>Diners</option>
						<option>Amex</option>
					</select>
				</span>
				<span>
					<input type="text" name="cc" id="cc">
				</span>
			</form>
			[:.
				valDict = {
					'cc': { mask: 
						[ { 
							mask: 'creditcard', 
							cardtype: 'Visa',
							cardSelect: pax.$('card_type')
						} ], 
						hint: 'This must be a valid VISA Credit card number' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<select name="card_type" id="card_type">
						<option value=''>Please select card type</option>
						<option value='1'>Mastercard</option>
						<option value='2'>Visa</option>
						<option value='3'>Diners</option>
						<option value='4'>Amex</option>
					</select>
				</span>
				<span>
					<input type="text" name="cc" id="cc">
				</span>
			</form>
			[:.
				valDict = {
					'cc': { mask: 
						[ { 
							mask: 'creditcard', 
							cardtype: 'Visa',
							cardSelect: pax.$('card_type'),
							cardSelectByText: true
						} ], 
						hint: 'This must be a valid VISA Credit card number' }
				};
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		Note that by setting cardSelectByText to ture, we use the option text, rather than the option value.
*/
pax.validate.validators.creditcard = function( field, mask ) {
	var fieldValid = true;
	var value = field.value;
	
	//	remove spaces and dashes
	value = value.split(' ').join('').split('-').join('');
	
	//	Critical validations
	if( ! pax.validate.validators.numeric( { value: value } ) )return false;
	var luhnValid = pax.validate.validators.luhn( { value: value } );
	if( ! luhnValid )return false;
	var cardType = ( mask.cardtype != null && mask.cardtype != '' )? mask.cardtype: null;
	
	//	Check if there is a select box with the card type
	var cardSelect = ( mask.cardselect != null )? mask.cardselect: null;
	if( pax.util.getType( cardSelect ) == 'string' )cardSelect = pax.$(cardSelect);
	var cardSelectByText = ( mask.cardselectbytext != null )? mask.cardselectbytext: null;
	if( cardSelect ) {
		//	TODO: Check that cardSelect is indeed a select, it could perhaps be a radio set too? if( indexOf( field.type, 'select' ) > -1 )...
		//	POSSIBLE SOLUTION: Use pax.form.getFieldValue?
		//	When the cardSelect box is changed, we revalidate the CC.
		pax.event.bindOne( cardSelect, ['change', 'keyup'], function() {
			pax.validate.validateField( null, field );
		} );
		
		var cardType = ( cardSelectByText )? 
			cardSelect.options[cardSelect.selectedIndex].text: 
			pax.form.getFieldValue( cardSelect ).toLowerCase();
	};
	
	if( cardType ) {
		cardType = cardType.toLowerCase();
		
		/*	Check the different card types.
			Test Credit Card Numbers:
			
			Visa: 		4111111111111111
			MasterCard: 5431111111111111
			Amex: 		341111111111111
			Discover: 	6011601160116611
		*/
		if( cardType == 'visa' ) {
			if( ! ( ( value.length == 16 || value.length == 13 ) && value.substring( 0, 1 ) == '4' ) )fieldValid = false;
		} else if( cardType == 'mastercard' ) {
			var sfx = value.substring( 0, 2 );
			if( ! ( value.length == 16 && ( pax.validate.validators.range( { value: sfx }, { min: 51, max: 55 } ) ) ) )fieldValid = false;
		} else if( cardType == 'amex' ) {
			var sfx = value.substring( 0, 2 );
			if( ! ( value.length == 15 && ( sfx == '34' || sfx == '37' ) ) )fieldValid = false;
		} else if( cardType == 'diners' ) {
			fieldValid = false;
			var sfx = value.substring( 0, 4 );
			//	Carte Blanche
			if( value.length == 14 && pax.validate.validators.range( { value: sfx.substring( 0, 3 ) }, { min: 300, max: 305 } ) )fieldValid = true;
			//	International
			if( value.length == 14 && sfx.substring( 0, 2 ) == '36' )fieldValid = true;
			//	US and Canada
			if( value.length == 16 && sfx.substring( 0, 2 ) == '55' )fieldValid = true;
		} else if( cardType == 'jcb' ) {
			fieldValid = false;
			var sfx = value.substring( 0, 4 );
			if( value.length == 15 && ( sfx == '1800' || sfx == '2131' ) )fieldValid = true;
			if( value.length == 16 && ( sfx == '35' ) )fieldValid = true;
		} else if( cardType == 'discover' ) {
			var sfx = value.substring( 0, 4 );
			if( ! ( value.length == 16 && ( sfx == '6011' || sfx.substring( 0, 2 ) == '65' ) ) )fieldValid = false;
		}
	}	

	return fieldValid;
};


/*	Method: pax.validate.validators.zip
	Validates a US Zip code
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="zip">
				</span>
			</form>
			[:.
				valDict = {
					'zip': { mask: 'zip', hint: 'This must be in zip format (NNNNN-NNNN)' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="zip" value="90210-5555">
			</span>
			<div id='pax.validate.validators.zip.example1'></div>
			[:.
				pax.$('pax.validate.validators.zip.example1').innerHTML = 'Is this a valid zip?: ' + pax.validate.validators.zip( pax.$('zip') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.zip = function( field, mask ) {
	return ( field.value.match(/^[0-9]{5}\-[0-9]{4}$/gi) )? true : false;
};


/*	Method: pax.validate.validators.domain
	Validates domain name
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<input type="text" name="domain">
				</span>
			</form>
			[:.
				valDict = {
					'domain': { mask: 'domain', hint: 'This must be a valid domain name' }
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the field, and display a hint with the specified text.

	Example:
		(start code)
			<span>
				<input type="text" id="domain" value="pointful.com">
			</span>
			<div id='pax.validate.validators.domain.example1'></div>
			[:.
				pax.$('pax.validate.validators.domain.example1').innerHTML = 'Is this a valid domain?: ' + pax.validate.validators.domain( pax.$('domain') );
			:]
		(end)
		Private usage, this will validate the fields value.
*/
pax.validate.validators.domain = function( field, mask ) {
	return ( field.value.match(/\b[A-Z0-9.-]+\.[A-Z]{2,4}\b/gi) )? true : false;
};


/*	Method: pax.validate.validators.ajaxValidate
	Validates via an ajax call. Note that your should assign any non-ajax validators first, when using an ajax validator.
	
	Parameters:
		field - a form field we want to validate

	Returns:
		true - boolean if the field validation passes, false otherwise

	Example:
		(start code)
			<form name='valForm' action='../pax.validate.example.validation.php'>
				<span>
					<textarea name="notes">These are my notes</textarea>
				</span>
			</form>
			[:.
				valDict = {
					'notes': { mask: [ { mask: 'ajaxValidate', 'method': 'notes'} ], hint: 'The notes are checked for some bad words on the server, try "firetruck"' }
				};

				pax.validate.initValidation( 'valForm', valDict );  
			:]
		(end)
		This sets a validator on the field, and displays a hint with the specified text.
		
		When validating, the server request hits the action of the form ('../pax.validate.example.validation.php'), with the following parameters:
			ajaxValidate - 1
			method - 'notes'
			formName - 'valForm'
			fieldName - 'notes'
			fieldValue - 'These are my notes' (or whatever notes are in the field)
			
		The server side function will then return the following values (in a JSON object) to the <pax.validate.ajaxValidateCallBack> method:
			error - string (if a validation error occured, it will contain a message that will be displayed for 5 seconds)
			formName - 'valForm'
			fieldName - 'notes'
			validField - boolean (if the field is valid, it will be true)
		
*/
pax.validate.validators.ajaxValidate = function( field, mask ) {
	if( typeof( pax.validate.ajaxValidatorQueue[field.formName] ) == 'undefined' ) {
		pax.validate.ajaxValidatorQueue[field.formName] = {};
		pax.validate.setTobeValidatedField( field );	// Assumes non-validated on first entry
	} else if( typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) == 'undefined' ) {
		pax.validate.setTobeValidatedField( field );	// Assumes non-validated on first entry
	}
	
	var postedAjax = false;
	if( pax.validate.ajaxValidatorQueue[field.formName] && typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) != 'undefined') {
		postedAjax = ( pax.validate.ajaxValidatorQueue[field.formName][field.name]['inProgress'] != false );
	};
	
	//	If the field hasn't been changed at all, we don't try and validate it.
	if( field.defaultValue == field.value )return false;
	
	//	Setup the initial posting, if we're not waiting for a callback.
	//	Note: the ajax callback will re-validate the field for us...
	if( ! postedAjax ) {
		var oldFieldValue = ( typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) != 'undefined' )? 
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['oldValue']: null;
		
		if( field.value != oldFieldValue ) {
			
			pax.validate.setTobeValidatedField( field );
				
			var validateAjaxField = function() {
				var url = ( typeof(mask.target) != 'undefined' )? mask.target: document.forms[field.formName].action;	//	Default to the form target, for server validation methods
				var ajaxMethod = ( typeof(mask.method) != 'undefined' )? mask.method: field.method;
				var post = 'ajaxValidate=1' + '&ajaxMethod=' + ajaxMethod + '&_program_mode=' + ajaxMethod + '&formName=' + field.formName + '&fieldName=' + field.name + '&fieldValue=' + field.value;
				var callBack = pax.validate.ajaxValidateCallBack;
				// check for field.ajaxCallBack here, for non-standard handler.
				if( typeof field.ajaxCallBack != 'undefined' )callBack = eval( field.ajaxCallBack );
				
				//	Set the status box spinner
				var pos = pax.util.getPosition( field );
				var spinnerBox = pax.box.showOnRight( field.formName + field.name + '_spinner', '', 'ajaxSpinner', field, 0 );
				pax.util.setPosition( spinnerBox, { x: ( pos.x + pos.width - 20 ), y: ( pos.y + 3 ) } );

				//	Hide error message
				pax.validate.hideFieldMessage( field );
				//	Make the call
				var rObject = pax.post( url, post, callBack, 'Ajax Validating ' + field.name, field.formName + field.name + '_spinner' );
				
				pax.validate.ajaxValidatorQueue[field.formName][field.name] = {
					oldValue: field.value, 
					robj: rObject, 
					mask: mask, 
					inProgress: true 
				};
				
				//	Clear the timeout - may not be necessary, but doesn't hurt.
				window.clearTimeout( pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] );
				pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] = null;
			};
			
			if( ! pax.validate.ajaxValidatorQueue[field.formName][field.name] ) {
				pax.validate.ajaxValidatorQueue[field.formName][field.name] = {};
			}

			//	Clear the timeout, and assign new timeout
			window.clearTimeout( pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] );
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['timeoutPointer'] = window.setTimeout( validateAjaxField, 300 );
			
			return true;
		}
	} else {
		// Try and validate field, without running ajax, and cancel ajax call if local validation fails.
		pax.validate.validateField( null, field, false );
		if( ! field.valid ) {
			if( pax.validate.ajaxValidatorQueue[field.formName][field.name] && pax.validate.ajaxValidatorQueue[field.formName][field.name]['robj'] ) {
				//	Cancel current ajax call, and reset queue items
				pax.cancel( pax.validate.ajaxValidatorQueue[field.formName][field.name]['robj'].ro );
			}
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['inProgress'] = false;
			pax.validate.ajaxValidatorQueue[field.formName][field.name]['oldValue'] = null;
			//	Revalidate, in case the field has changed
			pax.validate.validateField( null, field );
		}
	}
		
	return false;
};


/*	Private Method: pax.validate.ajaxValidateCallBack
	Ajax validation callback - handles response from the ajax validation; internal method only.
	
	Parameters:
		xml
		txt
		url

	Returns:
		true - boolean if the field validation passes, false otherwise

*/
pax.validate.ajaxValidateCallBack = function( xml, txt, url ) {
	var result = ( txt != '' )? pax.unJSON( txt ) : {};
	var error = result.error;
	var formName = result.formName;
	var fieldName = result.fieldName;
	var field = document.forms[formName][fieldName];
	var oldValue = (typeof pax.validate.ajaxValidatorQueue[formName][fieldName] != 'undefined')? pax.validate.ajaxValidatorQueue[formName][fieldName]['oldValue'] : null;

	//	Clear the progress flag
	pax.validate.ajaxValidatorQueue[formName][fieldName]['inProgress'] = false;

	//	Check if the value has changed while we were waiting for the callback, and run a new one if required
	if( oldValue != field.value && oldValue != null ) {
		// We take the mask from the queue, then remove the progress flag, and run the ajax validation again.
		var fieldMask = pax.validate.ajaxValidatorQueue[formName][fieldName].mask;
		pax.validate.validators.ajaxValidate( field, fieldMask );
	} else {
		//	We're done, and should set the field validity.
		if( result.validField == 1 ) {
			pax.validate.setValidField( field );
		} else {
			pax.validate.showFieldError( field, error );
			pax.validate.setInvalidField( field );
		}
		pax.validate.ajaxValidatorQueue[formName][fieldName]['validField'] = ( result.validField == 1 );
	}
};


/* ----------------------------------------------------------------------------
	The actual validator methods
---------------------------------------------------------------------------- */


pax.validate.showFieldError = function( field, message ) {
	if( field.showHint )field.showHint( message, pax.validate.errorMessageClass );
};


//
//	Hide the field message
//
pax.validate.hideFieldMessage = function( field ) {
	if( field.hideHint )field.hideHint();
};


//
//	Remove white space
//
pax.validate.removeWhiteSpace = function( value ) {
	value = value.split(' ').join('').split('\t').join('');
	return value;
};


//
//	Default white space by mask, or by def. If neither mask.whiteSpace nor def, we allow white space.
//	Note: this returns a value with white space removed, if we allow white space.
//
pax.validate.defaultWhiteSpace = function( value, mask, def ) {
	var allowWhiteSpace = true;
	
	if( pax.util.hasKey( 'whiteSpace', mask ) )allowWhiteSpace = ( mask.whiteSpace == true );
	else {
		//	Look to default
		if( def != 'undefined' ) {
			allowWhiteSpace = def;
		} else 	allowWhiteSpace = true;
	}
	
	return ( allowWhiteSpace )? pax.validate.removeWhiteSpace( value ): value;
};


//
//	returns the masked value - this could really be split up a bit better, ie: each mask field could be a function...
//
//	WIP - TODO: this does nothing yet.
//
pax.validate.maskEnforcer = function( value, forceMask ) {
	/*
		tmpMask = tmpMask.replace(/-/g,'\\-');
		tmpMask = tmpMask.replace(/S/g,'[A-Z]');
		tmpMask = tmpMask.replace(/N/g,'[0-9]');
		tmpMask = eval("/^" + tmpMask + "$/gi");
	*/
	
	var newValue = "";
	//	Allow free format masks - you can use S for a char string, N for a number and - for a dash
	for( i=0; i < value.length + 1; i++ ) {
		var tv = (i < value.length)?value.charAt(i).toUpperCase():'';
		
		if( forceMask.length >= i ) {
			var fm = forceMask[i];
			fm = fm.toUpperCase();	// IE sux!!!
			// Simply remove anything that does not match the mask.
			if( fm == 'S' ) {
				if( /[A-Z]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == 'N' ) {
				if( /[0-9]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == '-' ) {
				// if( tv == fm )newValue += value[i];
				// else newValue += '-';
				newValue += fm;
			}
		}
		else {
			if( fm == '-' )newValue += fm;
			//else newValue += ' ';
		}
	}
	
	/*
	for( i=0; i < forceMask.length; i++ ) {
		var fm = forceMask[i].toUpperCase();
		if( value.length >= i ) {
			var tv = value.charAt(i).toUpperCase();
			// Simply remove anything that does not match the mask.
			if( fm == 'S' ) {
				if( /[A-Z]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == 'N' ) {
				if( /[0-9]/.test( tv ) )newValue += value[i];
				// else newValue += ' ';
			}
			else if( fm == '-' ) {
				if( tv == fm )newValue += value[i];
				// else newValue += '-';
			}
		}
		else {
			if( fm == '-' )newValue += fm;
			//else newValue += ' ';
		}
	}
	*/
	
	/*
	WIP
	---
		Perhaps make the freemask a validator, instead of a stand-alone, which means we have a validator that can change the value (when applying the mask)
		


		//	This could work...!!! - Bind on "keypress" on the field
		- need to work out where inthe mask we are, and look at the field value too...

		function floatFilter(event) {
		    var keyPressed;
		    if (document.all) {
		        keyPressed = event.keyPressed; 
		    } else if (document.getElementById) {
		        keyPressed = event.which;   
		    } else if (document.layers) {
		        keyPressed = event.which;   
		    }

		    if (keyPressed >= 33 && keyPressed <= 43) {
		        return false;
		    } else if (keyPressed == 47) {
		        return false;
		    } else if (keyPressed >= 58 && keyPressed <= 126) {
		        return false;
		    } else {  
		        return true;     
		    }
		}



		
		
		
	*/

	return newValue;
};


//	TODO: We need a way to return the validation error message, see ipvalidator for an example of a message

//
//	Validates a field, and sets the surrounding countainer class.
//
pax.validate.validateField = function( e, field, useAjaxValidator ) {
	useAjaxValidator = ( typeof( useAjaxValidator ) != 'undefined' )? useAjaxValidator : true;
	if( !field )field = this;
	var fieldValid = true;

	if( field.mask ) {
		var fieldIsValid = true;
		//	Put ajax validations last, so that we don't try and run it if a client side validator fails first.
		//	Check if we have an array, or other sortable object.
		if( typeof( field.mask.sort ) == 'function' ) {
			field.mask.sort( function( a, b ) {
				var aMask = ( typeof(a.mask) != 'undefined' )? a.mask : a;
				var bMask = ( typeof(b.mask) != 'undefined' )? b.mask : b;
				if( ( aMask == 'ajaxValidate' && bMask == 'ajaxValidate' ) )return 0;
				else if( ( aMask == 'ajaxValidate' && bMask != 'ajaxValidate' ) )return 1;
				else return -1;
			} );
		}
		
		for( var msk in field.mask ) {
			// We assume an object here: { mask: 'MASK STRING', ATTRIBUTE1: VALUE, ATTRIBUTE@: VALUE }, eg: { mask: 'len', minLen: 3 }
			// Add the mask obj to the field
			var fieldMask = field.freeMask;
			if( typeof( field.mask[msk] ) == 'string' ) {
				if( field.mask[msk] == 'ajaxValidate' )fieldMask = field.mask;
				else fieldMask = { mask: field.mask[msk] };
			} else fieldMask = field.mask[msk];
	
			//	Check if we have a validator that matches specified mask
			var canValidate = false;
			for( var v in pax.validate.validators )if( v == fieldMask.mask )canValidate = true;
			
			if( canValidate ) {
				//	If we're using an ajax validator, we run just it, and return.
				//	This assumes all client side validation passed, as validators
				//	are sorted by clientside first, then ajax.
				if( fieldMask.mask == 'ajaxValidate' ) {
					//	Check if the ajax validation is in progress.
					if( useAjaxValidator ) {
						//	We only return if the ajaxvalidation returns true (ie: is in progress)
						if( pax.validate.validators[fieldMask.mask]( field, fieldMask ) )return false;
						//	Assume field is as per server response, or is not valid, if we don't have a response yet.
						if( pax.validate.ajaxValidatorQueue[field.formName] && pax.validate.ajaxValidatorQueue[field.formName][field.name] ) {
							fieldIsValid = pax.validate.ajaxValidatorQueue[field.formName][field.name]['validField'];
						} else fieldIsValid = false;
					}
				} else {
					// Run the validator, as long as we're not waiting for an ajax call.
					var postedAjax = false;
					if( typeof( pax.validate.ajaxValidatorQueue[field.formName] ) != 'undefined' && typeof( pax.validate.ajaxValidatorQueue[field.formName][field.name] ) != 'undefined') {
						if( pax.validate.ajaxValidatorQueue[field.formName][field.name]['inProgress'] != false )postedAjax = true;
						else postedAjax = false;
					}
					
					if( ! postedAjax ) {
						fieldIsValid = true;
						if( !pax.validate.validators[fieldMask.mask]( field, fieldMask ) ) {
							fieldIsValid = false;
							break;
						}
					} else fieldIsValid = false;
				}
			} else console.log( "Validator [" + fieldMask.mask + "] not found for field [" + field.name + "]" );
		}
		if( ! fieldIsValid )fieldValid = false;
	}
	
	//	Allow free format masks - you can use S for a char string, N for a number and - for a dash
	//	See above TODO: create a mask enforcer.
	if( field.freemask ) {
		for( var fmsk in field.freemask ) {
			var tmpMask = field.freemask[fmsk];
			
			tmpMask = tmpMask.replace(/-/g,'\\-');
			tmpMask = tmpMask.replace(/S/g,'[A-Z]');
			tmpMask = tmpMask.replace(/N/g,'[0-9]');
			tmpMask = eval("/^" + tmpMask + "$/gi");
			if( !field.value.match( tmpMask ) )fieldValid = false;
			
			// We would run the value mask enforcer here...
			// TODO: maskEnforcer to be developed still...
			if( !fieldValid ) {
				// pax.$('debugBox').value = pax.validate.maskEnforcer( field.value, field.freemask );
				// field.value = pax.validate.maskEnforcer( field.value, field.freemask );
			}
		}
	}

	//	We always validate the field if there is no "notEmpty" validator.
	var hasNotEmptyValidator = pax.util.hasValue( 'notEmpty', field.mask );
	if( ! hasNotEmptyValidator ) {	// Check for list / object
		if( pax.util.getType( field.mask ) == 'array' ) {
			for( var m in field.mask ) {
				if( field.mask[m]['mask'] == 'notEmpty' )hasNotEmptyValidator = true;
			}
		} else if ( typeof( field.mask ) == typeof( {} ) ) {
			if( field.mask['mask'] == 'notEmpty' )hasNotEmptyValidator = true;
		}
	}
	
	if( ! hasNotEmptyValidator && field.value.length == 0 && field.tagName == 'INPUT' )fieldValid = true;

	var fieldList = [];
	
	// Check for radio button here.
	if( field.type == 'radio' || field.type == 'checkbox' ) {
		if( typeof field.formName != 'undefined' ) {
			if( field.formName ) {
				rfields = document.forms[field.formName][field.name];
				for( var r = 0; r < rfields.length; r++ ) {
					fieldList[fieldList.length] = rfields[r];
				}
			}
		}
	} else fieldList[fieldList.length] = field;
	
	for( var fi = 0; fi < fieldList.length; fi++ ) {
		fld = fieldList[fi];
		//	Set the class of the field's surround div, and the validity.
		if( fieldValid )pax.validate.setValidField( fld );
		else pax.validate.setInvalidField( fld );
	}

	//	Show error
	if( ! fieldValid ) {
		if( fieldMask ) {
			if( pax.util.hasKey( 'message', pax.validate.validators[fieldMask.mask] ) ) {
				var tpl = pax.validate.validators[fieldMask.mask]['message'][false];
				var errorMessage = field.name + ' ' + pax.template.parse( tpl, fieldMask ).html;
				pax.validate.showFieldError( field, errorMessage );
			}
		}
	}
	
	return fieldValid;
};


//	Sets a field as valid
pax.validate.setValidField = function( field ) {
	if( ! pax.util.hasClassName( field, pax.validate.fieldValidClass ) ) {
		pax.util.addClassName( field, pax.validate.fieldValidClass );
	}
	pax.util.removeClassName( field, pax.validate.fieldToBeClass );
	pax.util.removeClassName( field, pax.validate.fieldInvalidClass );
	field.valid = true;
};

//	Sets a field as invalid.
pax.validate.setInvalidField = function( field ) {
	if( ! pax.util.hasClassName( field, pax.validate.fieldInvalidClass ) ) {
		pax.util.addClassName( field, pax.validate.fieldInvalidClass );
	}
	pax.util.removeClassName( field, pax.validate.fieldValidClass );
	pax.util.removeClassName( field, pax.validate.fieldToBeClass );
	field.valid = false;
};

//	Sets a field as not validated yet: assumes it's not valid. Used for ajax validation
pax.validate.setTobeValidatedField = function( field ) {
	pax.util.removeClassName( field, pax.validate.fieldInvalidClass );
	pax.util.removeClassName( field, pax.validate.fieldValidClass );
	pax.util.addClassName( field, pax.validate.fieldToBeClass );
	field.valid = false;
};

//
//	This checks all fields, and sets focus on the first invalid field.
//	If the optional formName has been specified, it will only check the
//	fields in that form.
//
pax.validate.isFormValid = function( formName ) {
	if( typeof formName != 'undefined' )myFields = pax.validate.allFields( formName );
	else myFields = pax.validate.allFields();
	
	// for( var f in myFields ){
	for( var f = 0; f < myFields.length; f++ ) {
		field = myFields[f];
		pax.validate.validateField( null, field );	// Re-validate the field
		
		if( !field.valid == true ) {
			//	TODO: Be able to configure an alert or modal dialog here,
			//	TODO: Configure if we want ALL false fields mentioned instead of just the first one.
			//	TODO: Be able to use the hint instead of the generated error message (default behaviour?)
			
			var errorMessage = (field.hint)? field.hint: "Please enter a valid value for: " + field.name;
			
			if( pax.validate.showAlertOnError )alert( errorMessage );
			else {
				pax.validate.showFieldError( field, errorMessage );
			} if( ( typeof field.type != 'undefined' ) ) {
				field.focus();
			}
			return false;
		}
	}

	return true;
};

//
//	Returns an array of all currently generically supported fields: input, select, textearea.
//	the formName is optional, however it is required to get the radio validations to work.
//	If form name is not specified, all supported form fields in the DOM will be returned.
//
pax.validate.allFields = function( formName, includeHidden ) {
	myFields = ( typeof formName != 'undefined' )? document.forms[formName].getElementsByTagName('*'): document.getElementsByTagName('*');
	var fields = new Array();

	//	Note that doing a "for(f in selectBoxes)" does NOT return the same as the below 'for' loop.
	for( var i = 0; i < myFields.length; i++ ) {
		var field = myFields[i];
		var fieldType = ( typeof field.nodeName != 'undefined' )? field.nodeName.toLowerCase(): '';
		
		if( fieldType == 'input' || fieldType == 'textarea' || ( fieldType.indexOf( 'select' ) > -1 ) ) {
			//	do not handle fields without a name, or hidden fields
			if( field.name == '' || field.name == 'undefined' )continue;
			if( ! includeHidden )if( field.type == 'hidden' )continue;
			fLen = fields.length;
			fields[fLen] = field;
			fields[fLen].formName = ( typeof formName != 'undefined' )? formName : false;
		}
	}

	return fields;
};


/*	Method: pax.validate.initValidation
	Initialises all form variables, and sets validation events

	Example:
		(start code)
			<form name='valForm'>
				<span>
					<label for="email">Email:</label>
					<input type="text" name="email">
				</span>
			</form>
			[:.
				valDict = {
					'email': { 
						mask: [
							{ mask: 'email' },
							{ mask: 'notEmpty' }
						],
						hint: 'The email address is mandatory, and must be a valid email address' 
					}
				};  
				pax.validate.initValidation( 'valForm', valDict );
			:]
		(end)
		This would set a validator on the email field, and display a hint with the specified text.
	
*/
pax.validate.initValidation = function( formName, valDict ) {
	var myFields = ( typeof formName != 'undefined' )? pax.validate.allFields( formName ): pax.validate.allFields();
	var valDict = ( typeof valDict == 'undefined' )? false: valDict;
	var radioSet = [];

    //  TODO: Allow these to be overridden in validation init
    var defaultHintArgs = {
        events: ['mouseover', 'focus'],
        hideEvents: ['mouseout', 'blur']
    };

	if( valDict ) {
		// Iterate through the form fields, and set the mask, freemask, required and formName attributes.
		for( var f = 0; f < myFields.length; f++ ) {
			field = myFields[f];
			for( var key in valDict ) {
				if( key == field.name ) {
							
					field.formName = formName;
					// Set the attributes - Note: we need to just set the radio field on the first instance???
					if( field.type == 'radio' || field.type == 'checkbox' ) {
						var hasBeenSet = false;
						for( var rs = 0; rs < radioSet.length; rs++ )if( field.name == radioSet[rs] )hasBeenSet = true;
						if( !hasBeenSet )radioSet[radioSet.length] = field.name;
					}
					
					// Set the attributes from the validation dict
					var attribs = valDict[key];
					for( var att in attribs ) {
						// Check if we have a mask or freemask; they can be lists, so allow that.
						
						if( att == 'mask' || att == 'freemask' ) {
							if( pax.util.getType( attribs[att] ) == 'array' ) {
								field[att] = attribs[att];
							} else {
								// Insert a list, as expected by the validation method.
								if( ! field[att] )field[att] = [];
								field[att][field[att].length] = attribs[att];
							}
						} else field[att] = attribs[att];
						
						// Set the hints
						if( att == 'hint' ) {

							//	If hint is object pass it straight to the hint.init, else create a default config.
							var conf = ( pax.util.getType( attribs[att] ) == 'object' )? attribs[att]: {
								message: attribs[att],
								showDelay: null
							};

							//	TODO: could optmise by building up a set of hints, before init.
							var fieldHint = {};
							conf['target'] = field;
							fieldHint[ (field.id || field.name) ] = conf;

							pax.widget.hint.init( fieldHint, defaultHintArgs );
						}
					}
				}
			}
			
			// Check if we need to bind the validation methods - only include a field if we have at least one validation method
			if( field.mask || field.freemask ) {
				var fieldList = [];
				// Need to check for radio button here. If radio button, then loop on all the radio buttons with that name...
				if( field.type == 'radio' || field.type == 'checkbox' ) {
					if( typeof field.formName != 'undefined' ) {
						if( field.formName ) {
							rfields = document.forms[field.formName][field.name];
							for( var r = 0; r < rfields.length; r++ ) {
								fieldList[fieldList.length] = rfields[r];
								rfields[r].required = field.required;
								rfields[r].formName = field.formName;
								rfields[r].valid = false;
							}
						}
					}
				}
				else fieldList[fieldList.length] = field;

				for( var fi = 0; fi < fieldList.length; fi++ ) {
					var fld = fieldList[fi];
					
					// Add validation events
					pax.event.bind( fld, 'blur', 	function(e) { 
						pax.validate.validateField( e, this ) 
					} );
					
					pax.event.bindEventList( fld, ['change', 'paste', 'keyup', 'focus', 'click'], function(e) { pax.validate.validateField( e, this ) } );

					// Check if this is an ajaxvalidation
					for( var fm in fld.mask ) {
						var hasAjaxValidate = pax.util.hasKey( 'ajaxValidate', fld.mask[fm]['mask'] );
						if( ! hasAjaxValidate ) {	// Check for list / object
							if( pax.util.getType( fld.mask[fm] ) == 'array' ) {
								for( var m = 0; m < fld.mask[fm].length; m++ ) {
									if( fld.mask[fm][m]['mask'] == 'ajaxValidate' )hasAjaxValidate = true;
								}
							} else if ( pax.util.getType( fld.mask[fm] ) == 'object' ) {
								if( fld.mask[fm]['mask'] == 'ajaxValidate' )hasAjaxValidate = true;
							}
						}
					}

					if( hasAjaxValidate )pax.validate.validateField( false, fld, false );
					else pax.validate.validateField( false, fld );
				}
			}
			else pax.validate.setValidField( field );
		}
		if( pax.validate.preventSubmit ) {
			//	Bind the onsubmit to validate, and prevent submission if the form isn't valid.
			if( document.forms[field.formName] ) {
				pax.event.bind( document.forms[field.formName], 'submit', function(e) {
					if( ! pax.validate.isFormValid( formName ) ) {
						if( e.preventDefault )e.preventDefault();	//	Most browsers
						else e.returnValue = false;					//	IE
						return false;								//	Fallback?
					}
				} );
			}
		}
	}
};
